package com.anti.jdbc.core.executor;

import com.anti.commom.core.exception.DatabaseException;
import com.anti.jdbc.core.executor.builder.SQL;
import com.anti.jdbc.core.executor.meta.ElementResolver;
import com.anti.jdbc.core.executor.meta.EntityElement;
import com.anti.jdbc.core.executor.meta.FieldElement;
import com.anti.jdbc.core.executor.meta.ValueElement;
import com.anti.jdbc.core.executor.meta.type.FillDateTypeHandler;
import com.anti.jdbc.core.executor.pojo.InsertMode;
import com.anti.jdbc.core.executor.supporrt.JdbcBuilder;
import com.anti.jdbc.core.executor.supporrt.Jdbcs;
import com.anti.jdbc.core.executor.supporrt.StringUtil;
import com.anti.jdbc.core.executor.transform.setter.ValueSetter;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;


/**
 * @Description 新增执行器
 * @Author anTi
 * @Date 2021-3-13
 */
public class InsertExecutor  extends AbstractExecutor<Object> {

    /**
     * 实体对象
     */
    private final Object persistent;

    private final SQL sqlBuilder = SQL.BUILD();
    private List<ValueElement> valueElements;
    private boolean returnKey=false;
    private Object primaryKey = null;
    /**
     * 新增类型
     */
    private  String insertMode;

    public  InsertExecutor(JdbcBuilder jdbcBuilder, Object persistent) {
        super(jdbcBuilder);
        this.persistent = persistent;
    }
    public  InsertExecutor(JdbcBuilder jdbcBuilder, Object persistent, InsertMode insertMode) {
        super(jdbcBuilder);
        this.persistent = persistent;
        this.insertMode = insertMode.name();
    }

    public  InsertExecutor(JdbcBuilder jdbcBuilder, Object persistent,  boolean returnKey,InsertMode insertMode) {
        super(jdbcBuilder);
        this.persistent = persistent;
        this.returnKey = returnKey;
        this.insertMode = insertMode.name();
    }
    @Override
    public void prepare() {
        this.checkEntity(this.persistent.getClass());
        EntityElement entityElement = ElementResolver.resolve(this.persistent.getClass());
        this.valueElements =new LinkedList();
        this.sqlBuilder.INSERT_INTO(entityElement.getTable());
        for (FieldElement fieldElement: entityElement.getFieldElements().values()) {
            if(fieldElement.isTransientField()) {
                continue;
            }
            Object value = Jdbcs.invokeMethod(this.persistent, fieldElement.getReadMethod()
                    ,"entity：" + entityElement.getName() + " field：" + fieldElement.getName() + " Failure to obtain value");
            if (fieldElement.isPrimaryKey()) {
                value = super.generatedId(this.persistent,fieldElement, value);
                primaryKey=value;
            }
            if (fieldElement.isCreatedDateField()) {
                value= FillDateTypeHandler.fillDate(fieldElement);
            }
            if(StringUtil.isNotBlank(insertMode)){
                if(insertMode.equals(InsertMode.IGNORENULL.toString())&&null==value) {
                    continue;
                }
            }

            this.sqlBuilder.VALUES(fieldElement.getColumn(), "?");
            this.valueElements.add(new ValueElement(value,fieldElement.isClob(),fieldElement.isBlob()));
        }
    }


    @Override
    protected Object doExecute() throws DatabaseException {
        final String sql = this.sqlBuilder.toString();
        if(returnKey){
            KeyHolder keyHolder = new GeneratedKeyHolder();
            jdbcBuilder.update(new PreparedStatementCreator() {
                                   @Override
                                   public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                                       PreparedStatement ps = con.prepareStatement(sql.toString(), 1);
                                       int i = 1;
                                       for (ValueElement object : valueElements) {
                                           ps.setObject(i, object.getValue());
                                           i++;
                                       }
                                       return ps;
                                   }
                               },
                    keyHolder);
            return keyHolder.getKey()!=null?keyHolder.getKey():primaryKey;
        }else{
            return this.jdbcBuilder.update(sql,new ValueSetter(LOBHANDLER,this.valueElements));
        }
    }
}
